#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QThread>
#include <QDir>
#include <QFile>
#include <QFileDialog>
#include <QSettings>

#include <QProcess>

#include <QDebug>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    ui->mainToolBar->hide();
    ui->statusBar->hide();

    // detect the number of available CPU cores:
    int maxThreads = QThread::idealThreadCount();
    if (maxThreads < 1) { // it may be -1 if it cannot be detected.
        maxThreads = 1;
    }
    ui->spinBox_numCpus->setMaximum(maxThreads);

    // we may also want to do this...
    ui->spinBox_numCpus->setValue(maxThreads);

    qDebug() << "Max threads: " << maxThreads;

    QDir dir = QDir::homePath() + "/a.obj"; // will be named a.obj, or a.prt, by default in the home directory.

    // create obj save file path in the ui:
    ui->label_objFileLoc->setText(dir.path());

    // create the save file (to process) path in the ui:
    if (getSystem() == "Windows") {
        dir = QDir(QDir::homePath() + "/AppData/Roaming/.minecraft/saves/");
    }
    else if (getSystem() == "Mac") {
        dir = QDir(QDir::homePath() + "/Library/Application Support/minecraft/saves/");
    }
    else if (getSystem() == "Linux") {
        dir = QDir(QDir::homePath() + "/.minecraft/saves/");
    }
    ui->label_saveFileLoc->setText(dir.path());

    // resize the default splitter size to favor the message log window's size
//    QList<int> sizeList;
//    sizeList << 9000 << 1;
//    ui->splitter->setSizes(sizeList);

    connect(ui->radioButton_square, SIGNAL(clicked()), this, SLOT(controlClicked_updateState()));
    connect(ui->radioButton_rect, SIGNAL(clicked()), this, SLOT(controlClicked_updateState()));
    connect(ui->checkBox_faceCount, SIGNAL(clicked()), this, SLOT(controlClicked_updateState()));
    connect(ui->checkBox_omitBelow, SIGNAL(clicked()), this, SLOT(controlClicked_updateState()));
    connect(ui->checkBox_prt, SIGNAL(clicked()), this, SLOT(controlClicked_updateState()));

    connect(ui->pushButton_saveTo, SIGNAL(clicked()), this, SLOT(chooseObjSave()));
    connect(ui->pushButton_fromSave, SIGNAL(clicked()), this, SLOT(browseForSave()));
    connect(ui->pushButton_mcobjLoc, SIGNAL(clicked()), this, SLOT(chooseMcobj()));

    connect(ui->pushButton_process, SIGNAL(clicked()), this, SLOT(run_mcobj()));
    connect(ui->pushButton_stop, SIGNAL(clicked()), this, SLOT(stopRun()));

    QString codeFont = getSystem() == "Windows" ? "Courier New" : (getSystem() == "Mac" ? "Courier" : "Courier 10 Pitch");
    int codeFontSize = getSystem() == "Mac" ? 11 : 9;
    QFont font(codeFont, codeFontSize);
    ui->textBrowser_messageLog->setFont(font);

    ui->label_mcobjLoc->setFont(font);
    ui->label_saveFileLoc->setFont(font);
    ui->label_objFileLoc->setFont(font);

    QFontMetrics fm(ui->spinBox_sizeX->font());

    ui->spinBox_sizeX->setMinimumWidth(fm.averageCharWidth() * 6);
    ui->spinBox_sizeY->setMinimumWidth(fm.averageCharWidth() * 6);
    ui->spinBox_sizeXY->setMinimumWidth(fm.averageCharWidth() * 6);

    ui->label_seaLevel->setFont(QFont(ui->label_seaLevel->font().family(), ui->label_seaLevel->font().pointSize() - 1));

    readSettings();

    detectedMaxThisRun = false;
    runMax = 0;
}

MainWindow::~MainWindow()
{
    delete ui;
}

QString MainWindow::getSystem()
{
#ifdef Q_WS_X11
    return QString("Linux");
#endif

#ifdef Q_WS_MAC
    return QString("Mac");
#endif

#ifdef Q_WS_WIN
    return QString("Windows");
#endif
}

void MainWindow::setRunningState(bool running)
{
    ui->groupBox_centerChunk->setEnabled(!running);
    ui->groupBox_limitComplexity->setEnabled(!running);
    ui->groupBox_otherOptions->setEnabled(!running);
    ui->groupBox_sizeChunk->setEnabled(!running);

    ui->pushButton_fromSave->setEnabled(!running);
    ui->pushButton_mcobjLoc->setEnabled(!running);
    ui->pushButton_process->setEnabled(!running);
    ui->pushButton_saveTo->setEnabled(!running);

    ui->pushButton_stop->setEnabled(running);
}

void MainWindow::readSettings()
{
    QSettings settings("mcobj-gui", "MainWindow");

    ui->label_mcobjLoc->setText(settings.value("mcobjLoc", "(unknown)").toString());
    ui->label_objFileLoc->setText(settings.value("ojbLoc", ui->label_objFileLoc->text()).toString());
    ui->label_saveFileLoc->setText(settings.value("saveLoc", ui->label_saveFileLoc->text()).toString());

    int width = 10;
    int height = 10;
    QSize size = settings.value("size", QSize(width, height)).toSize();
    resize(size);

}

void MainWindow::writeSettings()
{
    QSettings settings("mcobj-gui", "MainWindow");
    settings.setValue("mcobjLoc", ui->label_mcobjLoc->text());
    settings.setValue("ojbLoc", ui->label_objFileLoc->text());
    settings.setValue("saveLoc", ui->label_saveFileLoc->text());
    settings.setValue("size", size());

}

// Protected closeEvent
void MainWindow::closeEvent(QCloseEvent *event)
{
    bool okToQuit = true;

    // set to false if we're in the middle of a conversion?

    if (okToQuit) {
        writeSettings();
        event->accept();
    }
    else {
        event->ignore();
    }
}

void MainWindow::controlClicked_updateState()
{
    ui->spinBox_sizeXY->setEnabled(ui->radioButton_square->isChecked());
    ui->spinBox_sizeX->setEnabled(!ui->radioButton_square->isChecked());
    ui->spinBox_sizeY->setEnabled(!ui->radioButton_square->isChecked());

    ui->spinBox_faceCount->setEnabled(ui->checkBox_faceCount->isChecked());
    ui->spinBox_omitBelow->setEnabled(ui->checkBox_omitBelow->isChecked());
    ui->label_seaLevel->setEnabled(ui->checkBox_omitBelow->isChecked());

    QFileInfo finfo = QFileInfo(ui->label_objFileLoc->text());
    if (ui->checkBox_prt->isChecked()) {
        ui->label_objFileLoc->setText(QString("%1/%2.prt").arg(finfo.path()).arg(finfo.completeBaseName()));
    }
    else {
        ui->label_objFileLoc->setText(QString("%1/%2.obj").arg(finfo.path()).arg(finfo.completeBaseName()));
    }
}

void MainWindow::browseForSave()
{
    QString dirName;

    QFileDialog dialog(this, "Select Minecraft save directory to convert...", ui->label_saveFileLoc->text());
    dialog.setFilter(QDir::Dirs);
    dialog.setFileMode(QFileDialog::Directory);

    if (dialog.exec()) {
        dirName = dialog.selectedFiles().first();
        ui->label_saveFileLoc->setText(dirName);
    }
}

void MainWindow::chooseObjSave()
{
    QString fileName;
    if (ui->checkBox_prt->isChecked()) {
        fileName = QFileDialog::getSaveFileName(this, "Save Output As...", ui->label_objFileLoc->text(), "Object (*.prt)");
    }
    else {
        fileName = QFileDialog::getSaveFileName(this, "Save Output As...", ui->label_objFileLoc->text(), "Object (*.obj)");
    }
    if (fileName == "") {return;}

    ui->label_objFileLoc->setText(fileName);

}

void MainWindow::chooseMcobj()
{
    QString fileName;

    QString startDir;
    if (QDir(ui->label_mcobjLoc->text()).exists()) {
        startDir = ui->label_mcobjLoc->text();
    }
    else {
        startDir = QDir::homePath();
    }

    QFileDialog dialog(this, "Select mcobj executable...", startDir);
    dialog.setFilter(QFlags<QDir::Filter>(QDir::Executable || QDir::Files));
    dialog.setFileMode(QFileDialog::ExistingFile);

    if (dialog.exec()) {
        fileName = dialog.selectedFiles().first();
        ui->label_mcobjLoc->setText(fileName);
    }

}

void MainWindow::run_mcobj()
{
    detectedMaxThisRun = false;
    runMax = 0;

    // start by constructing the string we'll pass as parameters:
    QStringList arguments;

    // How many cores to use while processing. Defaults to 1. Set to the number of cpu's in the machine.
    if (ui->spinBox_numCpus->value() != 1) {
        arguments.append(QString(" -cpu %1").arg(ui->spinBox_numCpus->value()));
        // the value SHOULD be legal, based on the limiting of the maximum in the constructor.
    }

    // Output a PRT file instead of OBJ
    if (ui->checkBox_prt->isChecked()) {
        arguments.append("-prt");
    }

    // Center the output to chunk cx and cz. Defaults to chunk 0,0. To calculate the chunk coords, divide the values given in Minecraft's F3 screen by 16
    if (ui->spinBox_centerX->value() != 0 && ui->spinBox_centerY->value() != 0) {
        arguments.append(QString("-cx %1 -cz %2").arg(ui->spinBox_centerX->value()).arg(ui->spinBox_centerY->value()));
    }

    if (ui->radioButton_square->isChecked()) {
        // Output a sized square of chunks centered on -cx -cz. -s 20 will output 20x20 area around 0,0
        arguments.append(QString("-s %1").arg(ui->spinBox_sizeXY->value()));
    }
    else {
        // Output a sized rectangle of chunks centered on -cx -cz. -rx 2 -rx 8 will output a 2x8 area around 0,0
        arguments.append(QString("-rx %1 -rx %2").arg(ui->spinBox_sizeX->value()).arg(ui->spinBox_sizeY->value()));
    }

    // Limit the face count (in thousands of faces)
    if (ui->checkBox_faceCount->isChecked()) {
        arguments.append(QString("-fk %1").arg(ui->spinBox_faceCount->value()));
    }

    // Omit all blocks below this height. Use 63 for sea level
    if (ui->checkBox_omitBelow->isChecked()) {
        arguments.append(QString("-y %1").arg(ui->spinBox_omitBelow->value()));
    }

    // Hide the bottom of the world
    if (ui->checkBox_hideBottom->isChecked()) {
        arguments.append("-hb");
    }

    // Gray; omit materials
    if (ui->checkBox_omitMaterials->isChecked()) {
        arguments.append("-g");
    }

    // Don't combine adjacent faces of the same block within a column
    if (ui->checkBox_combineFaces->isChecked()) {
        arguments.append("-bf");
    }

    // Output sides of chunks at the edges of selection. Sides are usually omitted
    if (ui->checkBox_outputSides->isChecked()) {
        arguments.append("-sides");
    }

    // Name for the obj file to write to. Defaults to a.obj
    arguments.append(QString("-o %1").arg(ui->label_objFileLoc->text()));

    // Name for the save directory to process.
    arguments.append(QString("%1").arg(ui->label_saveFileLoc->text()));

    QString program = QString("%1").arg(ui->label_mcobjLoc->text());

    ui->textBrowser_messageLog->append(QString("Starting with arguments:%1\n").arg(arguments.join(" ")));

    // We should now have the entire command string, minus the ./app.exe bit at the front.


    qDebug() << "Starting...";

    proc = new QProcess(this);
    connect(proc, SIGNAL(readyRead()), this, SLOT(readFromMcobj()));
    connect(proc, SIGNAL(finished(int)), this, SLOT(finishedRun()));

    proc->start(program, arguments);

    setRunningState(true);
}

void MainWindow::readFromMcobj()
{
    QString str = proc->readAll();

    QRegExp rx;
    if (detectedMaxThisRun) {
        rx = QRegExp("^\\s+(\\d+)/");

        int pos = rx.indexIn(str);
        if (pos > -1) {
            bool ok;
            qDebug() << rx.cap(1) << rx.cap(2);
            int curr = QString(rx.cap(1)).toInt(&ok);
            if (ok) {
                ui->progressBar->setMaximum(runMax);
                ui->progressBar->setValue(curr);

                if (runMax == curr) {
                    ui->progressBar->setValue(-1);
                }

            }
        }

    }
    else {
        rx = QRegExp("^\\s+(\\d+)/(\\d+)\\s");
        int pos = rx.indexIn(str);
        if (pos > -1) {
            bool ok;
            qDebug() << rx.cap(1) << rx.cap(2);
            int curr = QString(rx.cap(1)).toInt(&ok);
            if (ok) {
                int max = QString(rx.cap(2)).toInt(&ok);
                if (ok) {
                    detectedMaxThisRun = true;
                    runMax = max;

                    ui->progressBar->setMaximum(max);
                    ui->progressBar->setValue(curr);

                    if (max == curr) {
                        ui->progressBar->setValue(-1);
                    }
                }
            }
        }
    }

    ui->textBrowser_messageLog->append(str);
}

void MainWindow::finishedRun()
{
    ui->textBrowser_messageLog->append("Finished running.");
    setRunningState(false);

    proc->deleteLater();
}

void MainWindow::stopRun()
{
    proc->kill();
    ui->textBrowser_messageLog->append("Killed mcobj.");
    setRunningState(false);

    ui->progressBar->setValue(0);

    proc->deleteLater();
}










